﻿using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.IO;
using System.Collections.Generic;
using UnityEditor;
using UnityEditorInternal;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using System.Linq;
using System;

namespace zbitmapfont
{
	public class BitmapFontTool
	{
        [MenuItem("Assets/zbitmapfont/Rename")]
        public static void Rename()
        {
            foreach (var a in Selection.assetGUIDs)
            {
                var p = AssetDatabase.GUIDToAssetPath(a);
                if (!System.IO.File.Exists(p))
                {
                    continue;
                }

                var f = System.IO.Path.GetFileNameWithoutExtension(p);
                var c = ConvertToChar(f);
                InputDialog.ShowWindow(c.ToString(), (a) =>
                {
                    if(string.IsNullOrEmpty(a))
                    {
                        return;
                    }

                    a = ConvertToFileName(a[0]);
                    string b = $"{System.IO.Path.GetDirectoryName(p)}/{a}{System.IO.Path.GetExtension(p)}";  
                    if(System.IO.File.Exists(b))
                    {
                        Debug.LogError("file existed: " + b);
                        return;
                    }

                    System.IO.File.Move(p, b);
                    System.IO.File.Move(p + ".meta", b + ".meta");

                    AssetDatabase.Refresh();
                });
                break;
            }


            AssetDatabase.Refresh();
        }

        [MenuItem("Assets/zbitmapfont/CreateBitmapFont")]
        public static void CreateBitmapFont()
        {
            foreach (var a in Selection.assetGUIDs)
            {
                var p = AssetDatabase.GUIDToAssetPath(a);
                if (!System.IO.Directory.Exists(p))
                {
                    continue;
                }

                string destPath = p;
                if (!CreateBitmapFont(p, destPath))
                {
                    throw new System.Exception("CreateBitmapFont failed: " + p);
                }
            }


            AssetDatabase.Refresh();
        }

        [MenuItem("Assets/zbitmapfont/CreateFontAsset")]
		public static void CreateFontAsset()
		{
			foreach(var a in Selection.assetGUIDs)
			{
				var p = AssetDatabase.GUIDToAssetPath(a);
				if(!System.IO.Directory.Exists(p))
				{
					continue;
				}

				string destPath = p;
				if(!CreateBitmapFont(p, destPath))
                {
                    throw new System.Exception("CreateBitmapFont failed: " + p);
                }
				CreateBitmapFontMaterial(destPath);
                CreateFontAsset(destPath);
			}


			AssetDatabase.Refresh();
		}

        public static string GetCurrentScriptPath()
        {
            System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace(true);
            System.Diagnostics.StackFrame frame = stackTrace.GetFrame(0);
            string filePath = frame.GetFileName();
            return filePath;
        }

        static char ConvertToChar(string fileName)
        {
            char index = fileName[0];
            try
            {
                if (fileName.Length == 4)
                {
                    int temp = System.Convert.ToInt32(fileName, 16);
                    index = (char)temp;
                }
                else if (fileName.Length > 1 && fileName[1] == '_')
                {
                    if (fileName[0] == '_')
                    {
                        index = ' ';
                    }
                    else
                    {
                        index = (char)('A' - 'a' + index);
                    }
                }
            }
            catch(Exception e) { }

            return index;
        }

        static string ConvertToFileName(char c)
        {
            string fileName = c.ToString();
            try
            {
                if(System.IO.Path.GetInvalidFileNameChars().Contains(c))
                {
                    fileName = ((int)c).ToString("x4");
                }
                else if(c == ' ')
                {
                    return "__";
                }
                else if(c >= 'A' && c <= 'Z')
                {
                    fileName = (char)(c - 'A' + 'a') + "_";
                }
            }
            catch (Exception e) { }

            return fileName;
        }

        public static bool CreateBitmapFont(string srcPath, string destPath)
		{
            destPath = System.IO.Path.GetFullPath(destPath);

            var paths = System.IO.Directory.GetFiles(srcPath, "*.png", SearchOption.TopDirectoryOnly);
            List<int> chars = new();
            List<string> icons = new();

            var sumSize = 0;
            foreach (var path in paths)
            {
                var fileName = Path.GetFileNameWithoutExtension(path);

                int index = ConvertToChar(fileName);

                //int index = fileName[0];


                //if (fileName.Length == 4)
                //{
                //    int temp = System.Convert.ToInt32(fileName, 16);
                //    index = (char)temp;
                //}
                //else if (fileName.Length > 1 && fileName[1] == '_')
                //{
                //    if (fileName[0] == '_')
                //    {
                //        index = ' ';
                //    }
                //    else
                //    {
                //        index = (char)('A' - 'a' + index);
                //    }
                //}

                chars.Add(index);
                icons.Add($"icon=\"{System.IO.Path.GetFullPath(path)}\",{index},0,0,0");


                var tex = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
                sumSize += tex.width * tex.height;

            }

            var pngWidth = Mathf.Pow(2, Mathf.CeilToInt(Mathf.Log(Mathf.Sqrt(sumSize), 2)));
            //var pngWidth = Mathf.CeilToInt(Mathf.Sqrt(sumSize));


            var templateConfigStr = System.IO.File.ReadAllText($"{System.IO.Path.GetDirectoryName(GetCurrentScriptPath())}/../template.bmfc");


            

            for(; pngWidth <= 4096 ; pngWidth *= 2)
            { 
                var configStr = string.Format(templateConfigStr, string.Join(",", chars), string.Join("\n", icons), pngWidth, pngWidth);

                var tempPath = $"{destPath}.bmfc";

                System.IO.File.WriteAllText(tempPath, configStr);


                var exePath = $"{System.IO.Path.GetDirectoryName(GetCurrentScriptPath())}/../bmfont1.14a~/bmfont64.com";
                var param = $"-c \"{tempPath}\" -o \"{destPath}\"";


                var ret = CommandLineTool.RunCommand2(exePath, param, Application.dataPath);

                if (ret.ExitCode != 0)
                {
                    Debug.LogError(ret.StdErr);
                    Debug.LogError(ret.StdOut);
                }
                else
                {
                    int count = 0;
                    for(int i = 0; true; i++)
                    {
                        var pngPath = $"{destPath}_{i}.png";
                        if (!System.IO.File.Exists(pngPath))
                        {
                            break;
                        }
                        else
                        {
                            if(i > 0)
                            {
                                System.IO.File.Delete(pngPath);
                            }
                        }
                        count++;
                    }

                    if(count <= 0)
                    {
                        return false;
                    }

                    if(count == 1)
                    {
                        break;
                    }
                }
            }

            AssetDatabase.Refresh();

            {
                var pngPath = System.IO.Path.GetRelativePath(System.IO.Path.GetDirectoryName(Application.dataPath), $"{destPath}_0.png");
                var importer = TextureImporter.GetAtPath(pngPath) as TextureImporter;
                importer.mipmapEnabled = false;
                importer.alphaIsTransparency = true;
                importer.npotScale = TextureImporterNPOTScale.None;
                importer.SaveAndReimport();
            }

            return true;
		}

		static void CreateBitmapFontMaterial(string destPath)
		{
			string pngPath = destPath + "_0.png";
            Texture2D tex = AssetDatabase.LoadAssetAtPath(pngPath, typeof(Texture2D)) as Texture2D;

			Shader shader = Shader.Find("UI/Default");
			Material material = new Material(shader);
			material.mainTexture = tex;
			AssetDatabase.CreateAsset(material, destPath + ".mat");

			AssetDatabase.Refresh();
		}


		

		static void CreateFontAsset(string bitmapfontPath)
		{
			var fntPath = $"{bitmapfontPath}.fnt";
			var pngPath = $"{bitmapfontPath}_0.png";
            var matPath = $"{bitmapfontPath}.mat";

            Material mat = AssetDatabase.LoadAssetAtPath(matPath, typeof(Material)) as Material;
			Texture2D tex = AssetDatabase.LoadAssetAtPath(pngPath, typeof(Texture2D)) as Texture2D;

			var fntConfigStr = System.IO.File.ReadAllText(fntPath);
            StringReader sr = new StringReader(fntConfigStr);

			List<string> charStrs = new();

			while(true)
			{
				var line = sr.ReadLine();
				if(string.IsNullOrEmpty(line))
				{
					break;
				}

				if(line.StartsWith("char "))
				{
					charStrs.Add(line);


                }
            }

            CharacterInfo[] charInfos = new CharacterInfo[charStrs.Count];


			float fontSize = 0;

            for(int j = 0; j < charStrs.Count; j++)
            {
				var cStr = charStrs[j];

				Dictionary<string, int> charparams = new();
				var splitedStrings = cStr.Split(' ');
				foreach (var a in splitedStrings)
				{
					if (a.Contains('='))
					{
						var b = a.Split('=');
						var key = b[0];
						var value = b[1];
                        charparams.Add(key, int.Parse(value));
                    }
				}

				int index = charparams["id"];


                CharacterInfo charInfo = new CharacterInfo();
                charInfo.index = index;
				charInfo.advance = charparams["width"];
				{
                    Rect r = new Rect();
                    r.x = (float)charparams["x"] / tex.width;
                    //r.y = (float)(tex.height - charparams["y"] - charparams["height"]) / tex.height;
                    r.y = (float)(tex.height - charparams["y"]) / tex.height;
                    r.width = (float)charparams["width"] / tex.width;
                    r.height = -(float)charparams["height"] / tex.height;

					
                    charInfo.uvBottomLeft = new Vector2(r.x, r.y);
                    charInfo.uvBottomRight = new Vector2(r.x + r.width, r.y);
                    charInfo.uvTopLeft = new Vector2(r.x, r.y + r.height);
                    charInfo.uvTopRight = new Vector2(r.x + r.width, r.y + r.height);

					charInfo.minX = 0;
					charInfo.minY = charparams["height"] / 2;
					charInfo.maxX = charparams["width"];
                    charInfo.maxY = -charparams["height"] / 2;

                    charInfo.advance = charparams["xadvance"];

                    fontSize = charparams["height"];
                }

                //{
                //    Vector4 padding = TextureTool.getSpritePadding(info.pivot, info.rect, 1, sprite.rect);
                //    Rect r = new Rect();
                //    r.x = padding[0];
                //    r.y = padding[3];
                //    r.width = info.rect.width;
                //    r.height = info.rect.height;
                //    r.y = -r.y;
                //    r.height = -r.height;
                //    charInfo.vert = r;

                //    fontSize = info.rect.height + Mathf.Abs(padding[1]) + Mathf.Abs(padding[3]);
                //}
                charInfos[j] = charInfo;
            }

            string fontPath = bitmapfontPath + ".fontsettings";

            Font font = null;
            if (System.IO.File.Exists(fontPath))
            {
                font = AssetDatabase.LoadAssetAtPath<Font>(fontPath);
            }
            else
            {
                font = new Font();
            }
            
            font.material = mat;
            font.name = System.IO.Path.GetFileName(bitmapfontPath);
            font.characterInfo = charInfos;




            string metaStr = null;
            if (System.IO.File.Exists(fontPath + ".meta"))
            {
                metaStr = System.IO.File.ReadAllText(fontPath + ".meta");
            }

            if (System.IO.File.Exists(fontPath))
			{
                EditorUtility.SetDirty(font);
                AssetDatabase.SaveAssets();
				// System.IO.File.Delete(fontPath);
                // AssetDatabase.Refresh();
            }
            else
            {
                AssetDatabase.CreateAsset(font, fontPath);
            }



            AssetDatabase.Refresh();

            if (!string.IsNullOrEmpty(metaStr))
			{
				System.IO.File.WriteAllText(fontPath + ".meta", metaStr);
            }

            //         string content = File.ReadAllText(fontPath);
            //content = Regex.Replace(content, "(\\s+m_LineSpacing:\\s*)(.*)", "${1}" + fontSize);
            //content = Regex.Replace(content, "(\\s+m_FontSize:\\s*)(.*)", "${1}" + fontSize);
            //System.IO.File.WriteAllText(fontPath, content);
            //AssetDatabase.ImportAsset(fontPath);

            SerializedObject serializedFont = new SerializedObject(font);
            SerializedProperty serializedLineSpacing = serializedFont.FindProperty("m_LineSpacing");
            serializedLineSpacing.floatValue = fontSize;
            SerializedProperty serializedFontSize = serializedFont.FindProperty("m_FontSize");
            serializedFontSize.floatValue = fontSize;
            SerializedProperty serializedAscent = serializedFont.FindProperty("m_Ascent");
            serializedAscent.floatValue = fontSize / 2;
            SerializedProperty serializedDescent = serializedFont.FindProperty("m_Descent");
            serializedDescent.floatValue = -fontSize / 2;
            serializedFont.ApplyModifiedProperties();
		}

	}
}